/*
   
-  学习解析设备树语法

在根节点下加入下面设备数，解析

	/ {
		...
		// my devicetree node
		xydnode3399 {
			compatible = "xyd_3399";
			reg = <0xFF76037c 0x4
				0xFF77e000 0x4
				0xFF780004 0x4
				0xFF780000 0x4
			>;
			testprop,mytest;
			test_list_string = "red fish","green fish", "blue fish";
			gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>;
			interrupt-parent = <&gpio2>;
			interrupts = <7 IRQ_TYPE_EDGE_BOTH>; // 上升沿和下降沿触发
			key-irq-gpio = <&gpio2 7 IRQ_TYPE_EDGE_BOTH>; // GPIO2_A7
			xyd-led3 { //LED3
				gpios = <&gpio2 9 GPIO_ACTIVE_LOW>;
				label = "led3";
			};
		};
		...
	}

	/xydnode3399
	/xydnode3399/xyd-led3
	
*/


#include <linux/module.h>
#include <linux/init.h>
#include <linux/of.h> // of				
#include <linux/of_gpio.h> //gpio	
#include <linux/gpio.h>	
#include <dt-bindings/gpio/gpio.h>
#include <linux/of_irq.h>  // 中断函数头文件



/**
 *  优先匹配id_table > of_device_id > driver->name
 */
const struct platform_device_id mydriveer_id_table ={
         .name="xyd_3399",
};


const struct of_device_id of_match_table_id[] ={
       { .compatible = "xyd_3399" },
       {}
};
const struct of_device_id *mynode_match;
int size;
int num;
u32 value_u32;
u64 value_u64;
u32 out_value[2];
struct irq_data *my_irq_data;
u32 trigger_type;
int irq;



static int __init dt_init(void)
{
	int i, ret;
	struct device_node *np = NULL, *child_np = NULL,*mydevice_node=NULL;
	struct property *prop = NULL;
	u32 regdata[8];
	int gpiono;
	enum of_gpio_flags flag;
	
	//1.1.通过绝对路径获得设备节点对象
	np = of_find_node_by_path("/xydnode3399");
	if (np) {
		printk("name=%s\n", np->name);
		printk("full name=%s\n", np->full_name);

	} else {
		printk("of_find_node_by_path fail\n");
		return -EFAULT;
	}

	//1.2.通过节点名字来查找
	/*
	*  从哪个节点开始查找，NULL，从根节点 ,可以执行某一个节点，从改节点下找
	*/
	  np  = of_find_node_by_name(NULL, "xydnode3399");
	if (np) {
		printk("name2=%s\n", np->name);
		printk("full name2=%s\n", np->full_name);

	} else {
		printk("of_find_node_by_name fail\n");
		return -EFAULT;
	}

	

	//解析子节点
#if 0
//方法1，通过绝对路径获得子节点
	child_np = of_find_node_by_path("/xydnode3399/xyd-led3");
#else
//方法2,在父节点基础上解析
	child_np = of_get_child_by_name(np, "xyd-led3");
	// 获取父节点
	np = of_get_parent(child_np);
    printk("of_get_parent   %s\n", np->name);


	// 参数2： 从父节点下第几个子节点查找，NULL 第 1个 
	child_np = of_get_next_child(np, NULL);
	printk("of_get_next_child  %s\n", child_np->name);



	//	// 使用compatible值查找节点 和 device_type 查找值节点	
	//	   // 参数1：  struct device_nod* from	   从哪个节点开始查找	NULL 空节点
	//	   // 参数2：  const char* type			  NULL 要查找节点的device_type 属性 值 
	//	   // 参数3：  const char* compat		  要查找节点compatible 节点属性值
		mydevice_node=of_find_compatible_node(NULL ,NULL, "xyd_3399");
		printk("mydevice node is5 %s\n" , mydevice_node->name);
	
		// 参数1：  struct device_nod* from		从哪个节点开始查找  NULL 空节点
		// 参数2 ：	of_device_id 匹配表， 从这个匹配表查找节点 
		//根据给定的of_device_id匹配表在设备树中查找匹配的节点
		mydevice_node=of_find_matching_node_and_match(NULL , of_match_table_id, &mynode_match);
		printk("mydevice node is6 %s\n" ,mydevice_node->name);


	
#endif
//	if (child_np) {
//		printk("name=%s\n", child_np->name);
//		printk("full name=%s\n", child_np->full_name);
//		
//	} else {
//		printk("of_find_node_by_path fail\n");
//		return -EFAULT;
//	}



	  //******************************************************************************************* //

 /*
  * 属性解析
  *
  */

#if 1
      printk("**************************************  \n");
	  np  = of_find_node_by_name(NULL, "xydnode3399");

	//2.获取节点下的属性对象compatible 
	  //参数 compatible compatible 属性的长度
		prop = of_find_property(np, "compatible", &size);
		if (prop) {
			printk("name=%s\n", prop->name);
			printk("value=%s\n", (char *)prop->value);
	
		} else {
			printk("of_find_property fail\n");
			return -EFAULT;
		}



		
	
		// 2.解析空属性，判断空节点是否存在
		prop = of_find_property(np, "testprop,mytest", NULL);
		if (prop) {
			printk("name=%s\n", prop->name);
	
		} else {
			printk("of_find_property fail\n");
			return -EFAULT;
		}



		//		xydnode3399 {
		//				compatible = "xyd_3399";
		//				reg = <0xFF76037c 0x4
		//					0xFF77e000 0x4
		//					0xFF780004 0x4
		//					0xFF780000 0x4
		//				>;
		//			}

		
		//3.解析reg 属性
		/*
		[77912.471075] regdata[0]=0xff76037c
[77912.471449] regdata[1]=0x4
[77912.471704] regdata[2]=0xff77e000
[77912.472004] regdata[3]=0x4
[77912.472256] regdata[4]=0xff780004
[77912.472625] regdata[5]=0x4
[77912.472878] regdata[6]=0xff780000
[77912.473179] regdata[7]=0x4
*/ 
		ret = of_property_read_u32_array(np, "reg", regdata, 8);
		if (!ret) {
			for (i = 0; i < 8; i++) 
				printk("regdata[%d]=0x%x\n", i, regdata[i]);
		} else {
			printk("of_property_read_u32_array fail\n");
			return -EFAULT;
		}




		// 获取reg属性的元素数量  
		   //参数1:节点
		   //参数2：属性
		   //参数3： 这个属性单个元素的尺寸，大小
		   num = of_property_count_elems_of_size(mydevice_node, "reg", 4);
		   printk("reg num is2 %d\n", num);    //  reg num is2 8

		   

		
	
		//解析gpios 属性  设备树如何描述gpio 
		gpiono = of_get_named_gpio_flags(np, "gpios", 0, &flag);
		if (gpio_is_valid(gpiono)) {
			printk("gpiono=%d\n", gpiono);
			printk("flag== GPIO_ACTIVE_HIGH: %d\n", flag == GPIO_ACTIVE_HIGH);
		} else {
			printk("of_get_named_gpio_flags fail\n");
			return -EFAULT;
		}
	
		//方法2 
		//gpiono = of_get_named_gpio(np, "gpios", 0);
	
	 //解析gpio  key-irq-gpio 属性
		gpiono = of_get_named_gpio_flags(np, "key-irq-gpio", 0, &flag);
		if (gpio_is_valid(gpiono)) {
			printk("gpiono=%d\n", gpiono);
			printk("flag== IRQ_TYPE_EDGE_BOTH: %d\n", flag == IRQ_TYPE_EDGE_BOTH);
		} else {
			printk("of_get_named_gpio_flags fail\n");
			return -EFAULT;
		}


	
	gpiono = of_get_named_gpio_flags(child_np, "gpios", 0, &flag);
	if (gpio_is_valid(gpiono)) {
		printk("gpiono=%d\n", gpiono);
		printk("flag== GPIO_ACTIVE_LOW: %d\n", flag == GPIO_ACTIVE_LOW);
	} else {
		printk("of_get_named_gpio_flags fail\n");
		return -EFAULT;
	}


#endif 
//	 [87052.050959] xydnode3399 node is1 xydnode3399  
//	 [87052.056293] irq_of_parse_and_map is 141
//	 [87052.056641] trigger type is 0x3
//	 [87052.057261] of_irq_get	is 141


     mydevice_node = of_find_node_by_name(NULL, "xydnode3399");
     printk("xydnode3399 node is1 %s  \n", mydevice_node->name);   
     // 获取中断号4种方法
    // 解析和映射中断号
    irq = irq_of_parse_and_map(mydevice_node, 0);
    printk("irq_of_parse_and_map is %d\n", irq);   

	// 获取中断数据结构
	   my_irq_data = irq_get_irq_data(irq);
	// 获取中断触发类型
	   trigger_type = irqd_get_trigger_type(my_irq_data);
	   printk("trigger type is 0x%x\n", trigger_type);	 // 这个宏对应的值 IRQ_TYPE_EDGE_RISING= 0x1 

		 // 方式2:  从设备节点获取中断号
	   irq = of_irq_get(mydevice_node, 0);
	   printk("of_irq_get  is %d\n", irq);


	

	printk("call %s()\n", __func__);  
	return 0;
}


static void __exit dt_exit(void)
{
	
	printk("call %s() @ %d\n", __FUNCTION__, __LINE__);  
	
}


module_init(dt_init); 
module_exit(dt_exit); 
MODULE_LICENSE("GPL"); 


